#Required libraries

# Tidyverse for data science and exploration
require(dplyr)
require(tidyr)
require(readr)
require(tibble)
require(stringr)
require(purrr)
require(forcats)
require(rlang)

# enhances tidyverse
require(tidylog) # additional logging
require(magrittr) # additional data pipe syntax


# for reading data in multiple formats
require(readxl)
require(haven)

# visual analysis
require(ggplot2)
require(GGally) # extensions to ggplot
require(gt) # well formatted tables
# client-side interactive publishable graphics
require(plotly)
require(leaflet)
require(crosstalk)
require(htmlwidgets)
# server-side interactive graphics
require(shiny)
require(shinyjs)
# Canned Interactive EDA 
require(ExPanDaR)

Exploring KU Book Processing Charges

# read KU data frame
KUbpc.df <- read_csv("Public Data/openapc-de/data/bpc.csv")
Parsed with column specification:
cols(
  institution = col_character(),
  period = col_double(),
  euro = col_double(),
  doi = col_character(),
  backlist_oa = col_logical(),
  publisher = col_character(),
  book_title = col_character(),
  isbn = col_character(),
  isbn_print = col_character(),
  isbn_electronic = col_character(),
  license_ref = col_character(),
  indexed_in_crossref = col_logical(),
  doab = col_logical()
)
# read DOAB metadata

source('Public Data/DOAB/doabingest.R')
DOABmeta.df <- doabFetch()
embedded nul(s) found in input


head(KUbpc.df)
head(summary(KUbpc.df))
 institution            period          euro          doi           
 Length:938         Min.   :2017   Min.   :1075   Length:938        
 Class :character   1st Qu.:2017   1st Qu.:1875   Class :character  
 Mode  :character   Median :2018   Median :1981   Mode  :character  
                    Mean   :2018   Mean   :4368                     
                    3rd Qu.:2019   3rd Qu.:8250                     
                    Max.   :2020   Max.   :8978                     
 backlist_oa      publisher          book_title            isbn          
 Mode :logical   Length:938         Length:938         Length:938        
 FALSE:357       Class :character   Class :character   Class :character  
 TRUE :581       Mode  :character   Mode  :character   Mode  :character  
                                                                         
                                                                         
                                                                         
  isbn_print        isbn_electronic    license_ref       
 Length:938         Length:938         Length:938        
 Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character  
                                                         
                                                         
                                                         
 indexed_in_crossref    doab        
 Mode :logical       Mode :logical  
 FALSE:127           FALSE:44       
 TRUE :811           TRUE :894      
                                    
                                    
                                    
ggplot(data = KUbpc.df, aes(KUbpc.df$institution)) + geom_bar() 


ggplot(data = KUbpc.df, aes(KUbpc.df$euro)) + geom_histogram()

General Exploratory Data Analysis


ggplot(data = KUbpc.df) + geom_bar(mapping = aes(x = KUbpc.df$doab))


# Date to Doab
date_doab <- KUbpc.df %>% ggplot(data = KUbpc.df, mapping = aes(x = KUbpc.df$period, colour = KUbpc.df$doab)) + geom_freqpoly(binwidth = 0.1)
ggplotly(date_doab)


# publisher_euro <- KUbpc.df %>% 
# ggplot(data = KUbpc.df, mapping = aes(x = KUbpc.df$publisher, colour = KUbpc.df$euro)) + geom_freqpoly(binwidth = 0.1)

# Institution to Euro
institution_euro <- KUbpc.df %>% ggplot(data = KUbpc.df, mapping = aes(x = KUbpc.df$euro)) + geom_freqpoly(mapping = aes(colour = KUbpc.df$institution), binwidth = 500)

ggplotly(institution_euro)

NA

Idea: Publishers vs. Charges

Question: How do the top 25% of publishers divide up charges (in Euro)?

Observation: Charges are grouped around ~2000 Euros and ~8000 Euros.


publisher_counts <- KUbpc.df %>%
    group_by(publisher) %>%
    tally
tally: now 110 rows and 2 columns, ungrouped
sorted_counts = arrange(publisher_counts, desc(n))

total_n = sum(sorted_counts$n)
quarter_n = 0.25 * total_n
new_n = sum(sorted_counts$n[0:6])

sorted_counts %>% filter(n > 24)

# filtered <- filter(KUbpc.df$publisher %in% sorted_counts$publisher)

filtered <- filter(KUbpc.df, KUbpc.df$publisher == 'transcript Verlag' |
                     KUbpc.df$publisher == 'Duke University Press' |
                     KUbpc.df$publisher == 'University of Michigan Press' |
                     KUbpc.df$publisher == 'Manchester University Press' |
                     KUbpc.df$publisher == 'Pluto Press' |
                     KUbpc.df$publisher == 'Liverpool University Press')

head(filtered)

euro_publisher <- filtered %>% 
  ggplot(data = filtered, mapping = aes(x = filtered$publisher, y = filtered$euro), 
         aes(x = filtered$publisher, y = filtered$euro)) + 
  # geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) + 
  geom_count(aes(color = ..n.., group = euro)) + 
  scale_size_area(max_size = 10) + 
  theme(axis.text = element_text(size = rel(0.75))) +
  labs(title = "How Publishers Divide Charges", x = "Top 25% of Publishers", y = "Price (Euro)", color = 'Number of Copies') +
  scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17))

# ggplot:
ggplotly(euro_publisher)


# crosstalk:
ft <- highlight_key(filtered)
gg_ft <- ggplot(data = ft, mapping = aes(x = filtered$publisher, y = filtered$euro)) + 
  geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) + 
  labs(title = "How Publishers Divide Charges", x = "Top 25% of Publishers", y = "Price (Euro)", color = 'Number of Copies') +
  scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17))
cross_ft <- bscols(
  filter_select("publisher", "Select a publisher", ft, ~publisher),
  ggplotly(gg_ft, dynamicTicks = TRUE),
  widths = c(12, 12)
)
All elements of `...` must be named.
Did you want `key = c(key)`?Sum of bscol width units is greater than 12
bscols(cross_ft)


# shared_euro_publisher <- SharedData$new(filtered)
# leaflet(shared_euro_publisher) %>% addMarkers()
# data.table::data.table(shared_euro_publisher)

Idea: Publishers’ Charges vs. Year/OA Type

Sub-Question: What best explains the particular division of charges? (Year, OA Type)

Observation: The low and high charge groups seem to be defined by the type of OA business model, whereas the slight differences within each group seem to be defined by the year.


head(filtered)

# Does Type of OA impact the particular division of charges?

euro_oa_publisher <- filtered %>% 
  ggplot(data = filtered, mapping = aes(x = filtered$backlist_oa, y = filtered$euro), 
         aes(x = filtered$backlist_oa, y = filtered$euro)) + 
  geom_count(aes(color = ..n.., group = euro)) + 
  scale_size_area(max_size = 10) + 
  theme(axis.text = element_text(size = rel(0.75))) +
  labs(title = "How OA Impacts Price Division of Charges", x = "Type of OA", y = "Price (Euro)", color = 'Number of Copies')

# ggplot:
ggplotly(euro_oa_publisher)


# crosstalk:
ft <- highlight_key(filtered)
gg_ft <- ggplot(data = ft, mapping = aes(x = filtered$backlist_oa, y = filtered$euro)) + 
  geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) + 
  labs(title = "How OA Impacts Division of Charges", x = "Type of OA", y = "Price (Euro)", color = 'Number of Copies')
cross_ft <- bscols(
  filter_select("publisher", "Select a publisher", ft, ~publisher),
  ggplotly(gg_ft, dynamicTicks = TRUE),
  widths = c(12, 12)
)
All elements of `...` must be named.
Did you want `key = c(key)`?Sum of bscol width units is greater than 12
bscols(cross_ft)



# Does Year impact the particular division of charges?

euro_year_publisher <- filtered %>% 
  ggplot(data = filtered, mapping = aes(x = filtered$period, y = filtered$euro), 
         aes(x = filtered$period, y = filtered$euro)) + 
  geom_count(aes(color = ..n.., group = euro)) + 
  scale_size_area(max_size = 10) + 
  theme(axis.text = element_text(size = rel(0.75))) +
  labs(title = "How Year Impacts Price Division of Charges", x = "Year", y = "Price (Euro)", color = 'Number of Copies')

# ggplot:
ggplotly(euro_year_publisher)


# crosstalk:
ft <- highlight_key(filtered)
gg_ft <- ggplot(data = ft, mapping = aes(x = filtered$period, y = filtered$euro)) + 
  geom_count(aes(color = ..n.., size = after_stat(prop), group = euro)) + 
  labs(title = "How Year Impacts Division of Charges", x = "Year", y = "Price (Euro)", color = 'Number of Copies')
cross_ft <- bscols(
  filter_select("publisher", "Select a publisher", ft, ~publisher),
  ggplotly(gg_ft, dynamicTicks = TRUE),
  widths = c(12, 12)
)
All elements of `...` must be named.
Did you want `key = c(key)`?Sum of bscol width units is greater than 12
bscols(cross_ft)

NA
NA

Idea: Publishers vs. OA

Question: What type of business model do the top 25% publishers use?

Observation: Most have a higher proportion of True (moved to OA from traditional publishing) than False (already published OA).


oa_type <- filtered %>% 
  ggplot(data = filtered, mapping = aes(x = filtered$publisher, colour = filtered$backlist_oa), fill = filtered$backlist_oa) +
  geom_bar(position = "fill", width = 0.7, fill="#EAEAEA") +
  labs(title = "Business Model OA for Publishers", x = "Top 25% of Publishers", y = "Proportion", color = 'Types of OA') +
  theme(axis.text = element_text(size = rel(0.75))) +
  scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17)) +
  scale_color_brewer(palette = "Set1")

ggplotly(oa_type)


# crosstalk:
ft <- highlight_key(filtered)
oa_ft <- ggplot(data = ft, mapping = aes(x = ft$publisher, colour = ft$backlist_oa), fill = ft$backlist_oa) +
  geom_bar(position = "fill", width = 0.7) +
  labs(title = "Business Model OA for Publishers", x = "Top 25% of Publishers", y = "Proportion of Backlist OA", color = 'Types of OA') +
  theme(axis.text = element_text(size = rel(0.75))) +
  scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17))
# cross_oa_ft <- bscols(
#   filter_select("publisher", "Select a publisher", ft, ~publisher),
#   ggplotly(oa_ft, dynamicTicks = TRUE),
#   # widths = c(12, 12)
# )

# bscols(cross_oa_ft)

Idea: Publishers’ OA vs. Year

Question: Did OA business models of the top 25% publishers change per year?

Observation:


oa_time <- function(pub_name) {
  pub_ft <- filter(filtered, filtered$publisher == pub_name)
  
  pub_oa <- pub_ft %>% 
    ggplot(data = pub_ft, mapping = aes(x = pub_ft$period, colour = pub_ft$backlist_oa), fill = pub_ft$backlist_oa) +
    geom_bar(position = "fill", width = 0.7, fill="#EAEAEA") +
    labs(title = paste(pub_name, "'s OA Through the Years", sep = ""), 
         x = "Years", y = "Proportion of Backlist OA", color = 'Types of OA') +
    theme(axis.text = element_text(size = rel(0.75))) +
    scale_x_discrete(limits=c(2017, 2018, 2019)) +
    scale_color_brewer(palette = "Set1")

  ggplotly(pub_oa)
  
}

top25_list = c("transcript Verlag", "Duke University Press", "University of Michigan Press", "Manchester University Press", "Pluto Press", "Liverpool University Press")

oa_time("transcript Verlag")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

oa_time("Duke University Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

oa_time("University of Michigan Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

oa_time("Manchester University Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

oa_time("Pluto Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

oa_time("Liverpool University Press")
Continuous limits supplied to discrete scale.
Did you mean `limits = factor(...)` or `scale_*_continuous()`?

Idea: Revenue vs. OA

Question: What total revenue are publishers receiving each year?

Observation:


# Finding total revenue for each publisher

revenue_finder <- function(pub_name) {
  pub_filtered <- filter(filtered, filtered$publisher == pub_name)
  rev = sum(pub_filtered$euro)
}

revenue_df <- data.frame("publisher" = top25_list)
revenue_list <- c()

for (i in top25_list) {
  revenue_list<-c(revenue_list,revenue_finder(i))
}

revenue_df$revenue <- c(revenue_list)
print(revenue_df)

# ggplot:
publisher_revenue <- revenue_df %>%
  ggplot(data = revenue_df, mapping = aes(x = revenue_df$publisher, y = revenue_df$revenue), fill = revenue_df$revenue) +
  geom_col() +
  labs(title = "Total Revenue for Publishers", x = "Top 25% of Publishers", y = "Revenue (Euro)", color = 'Types of OA') +
  theme(axis.text = element_text(size = rel(0.75))) +
  scale_x_discrete(labels = function(x) str_wrap(str_replace_all(x, "foo", " "), width = 17)) +
  scale_fill_brewer(palette = "Set1")

ggplotly(publisher_revenue)
Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.Use of `revenue_df$revenue` is discouraged. Use `revenue` instead.

Idea: Revenue vs. OA

Question: What revenue are publishers receiving per year?

Observation:


# Finding total revenue for each publisher

revlist_2017 <- c()
revlist_2018 <- c()
revlist_2019 <- c()

revlist <- c()

for (name in top25_list) {
  pub_name <- filter(filtered, filtered$publisher == name)
  rev_2017 = sum(pub_name[pub_name$period == 2017,]$euro)
  revlist_2017 <- c(revlist_2017, rev_2017)
  rev_2018 = sum(pub_name[pub_name$period == 2018,]$euro)
  revlist_2018 <- c(revlist_2018, rev_2018)
  rev_2019 = sum(pub_name[pub_name$period == 2019,]$euro)
  revlist_2019 <- c(revlist_2019, rev_2019)
}

revenue_df <- data.frame("publisher" = top25_list)
revenue_df$'2017' <- c(revlist_2017)
revenue_df$'2018' <- c(revlist_2018)
revenue_df$'2019' <- c(revlist_2019)

print(revenue_df)

revenue_year <- c(revenue_df$'2017', revenue_df$'2018', revenue_df$'2019')
year <- c('2017', '2018', '2019')

# ggplot:
pub_year_revenue1 <- revenue_df %>%
  
  ggplot(data = revenue_df, mapping = aes(x = '2017', y = revenue_df$'2017', fill = revenue_df$publisher)) +
  geom_bar(position="dodge", stat="identity") +
  labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
  theme(axis.text = element_text(size = rel(0.75)))

ggplotly(pub_year_revenue1)
Use of `revenue_df$"2017"` is discouraged. Use `2017` instead.Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.

pub_year_revenue2 <- revenue_df %>%
  
  ggplot(data = revenue_df, mapping = aes(x = '2018', y = revenue_df$'2018', fill = revenue_df$publisher)) +
  geom_bar(position="dodge", stat="identity") +
  labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
  theme(axis.text = element_text(size = rel(0.75)))

ggplotly(pub_year_revenue2)
Use of `revenue_df$"2018"` is discouraged. Use `2018` instead.Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.

pub_year_revenue3 <- revenue_df %>%
  
  ggplot(data = revenue_df, mapping = aes(x = '2019', y = revenue_df$'2019', fill = revenue_df$publisher)) +
  geom_bar(position="dodge", stat="identity") +
  labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
  theme(axis.text = element_text(size = rel(0.75)))

ggplotly(pub_year_revenue3)
Use of `revenue_df$"2019"` is discouraged. Use `2019` instead.Use of `revenue_df$publisher` is discouraged. Use `publisher` instead.

Continued, tried putting it into one graph.


revlist <- c()
revlist_2017 <- c()
revlist_2018 <- c()
revlist_2019 <- c()

for (name in top25_list) {
  pub_name <- filter(filtered, filtered$publisher == name)
  rev_2017 = sum(pub_name[pub_name$period == 2017,]$euro)
  revlist_2017 <- c(revlist_2017, rev_2017)
  rev_2018 = sum(pub_name[pub_name$period == 2018,]$euro)
  revlist_2018 <- c(revlist_2018, rev_2018)
  rev_2019 = sum(pub_name[pub_name$period == 2019,]$euro)
  revlist_2019 <- c(revlist_2019, rev_2019)
}

revlist <- c(revlist_2017, revlist_2018, revlist_2019)

print(revlist)
 [1] 127420 153491  43044  96849  51824  48131  58125  61875  54750  36000
[11]  53625  21375  51750  58125  36000  17625  40500  42375
nrev <- matrix(revlist, ncol=6, byrow=TRUE)
colnames(nrev) <- top25_list
rownames(nrev) <- c("2017", "2018", "2019")
nrev <- as.table(nrev)
nrev <- as.data.frame.matrix(nrev)

print(nrev)

#, nrev$`Duke University Press`, nrev$`University of Michigan Press`, nrev$`Pluto Press`, nrev$`Manchester University Press`, nrev$`Liverpool University Press`

pub_year_rev <- nrev %>%
  
  ggplot(data = nrev, mapping = aes(x = c("2017", "2018", "2019"), y = c(nrev$"transcript Verlag"), fill = nrev$publisher)) +
  geom_bar(position="dodge", stat="identity") +
  labs(title = "Total Revenue for Publishers", x = "Year", y = "Revenue (Euro)", color = 'Publishers') +
  theme(axis.text = element_text(size = rel(0.75)))

ggplotly(pub_year_rev)
Use of `nrev$"transcript Verlag"` is discouraged. Use `transcript Verlag` instead.Use of `nrev$publisher` is discouraged. Use `publisher` instead.

Idea: DOAB analysis

Question: What is the average time gap between year of publication and added on date?

Observation:


DOABmeta.df <- filter(DOABmeta.df, is.na(DOABmeta.df$Year.of.publication))
print(DOABmeta.df$Year.of.publication[1:4])
[1] NA NA NA NA
gap = mean(DOABmeta.df$Added.on.date - DOABmeta.df$Year.of.publication[1:3])
Error in DOABmeta.df$Added.on.date - DOABmeta.df$Year.of.publication[1:3] : 
  non-numeric argument to binary operator

Comparison of charges by year and backlist

# create faceted plot object
charges.plot <- KUbpc.df %>% ggplot(aes(euro))+geom_histogram(bins=6)+facet_grid(rows=vars(period), cols = vars(backlist_oa))


## Present as Standard plot
 plot(charges.plot)


# this plot will render publicly https://htmlpreview.github.io/?https://github.com/MIT-Informatics/monograph/blob/master/00%20EDA%20Start.nb.html

Interactive charges exploration

 ggplotly(charges.plot)

# https://mit-informatics.github.io/monograph/demo.html
### Interactive Dataset Exploration 
KUbpc.df %>% ExPanD(df=.       ,title="KU Book Processing Charges",export_nb_option = TRUE)
# ExPanD uses shiny() which works running R locally, but isn't going to work through github. Could publish through shinyapps.io (low usage only), or export  a non-interactive notebook it
# see: https://drmaltman.shinyapps.io/demo/
LS0tDQp0aXRsZTogIkV4cGxvcmF0b3J5IEFuYWx5c2lzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3J9DQojUmVxdWlyZWQgbGlicmFyaWVzDQoNCiMgVGlkeXZlcnNlIGZvciBkYXRhIHNjaWVuY2UgYW5kIGV4cGxvcmF0aW9uDQpyZXF1aXJlKGRwbHlyKQ0KcmVxdWlyZSh0aWR5cikNCnJlcXVpcmUocmVhZHIpDQpyZXF1aXJlKHRpYmJsZSkNCnJlcXVpcmUoc3RyaW5ncikNCnJlcXVpcmUocHVycnIpDQpyZXF1aXJlKGZvcmNhdHMpDQpyZXF1aXJlKHJsYW5nKQ0KDQojIGVuaGFuY2VzIHRpZHl2ZXJzZQ0KcmVxdWlyZSh0aWR5bG9nKSAjIGFkZGl0aW9uYWwgbG9nZ2luZw0KcmVxdWlyZShtYWdyaXR0cikgIyBhZGRpdGlvbmFsIGRhdGEgcGlwZSBzeW50YXgNCg0KDQojIGZvciByZWFkaW5nIGRhdGEgaW4gbXVsdGlwbGUgZm9ybWF0cw0KcmVxdWlyZShyZWFkeGwpDQpyZXF1aXJlKGhhdmVuKQ0KDQojIHZpc3VhbCBhbmFseXNpcw0KcmVxdWlyZShnZ3Bsb3QyKQ0KcmVxdWlyZShHR2FsbHkpICMgZXh0ZW5zaW9ucyB0byBnZ3Bsb3QNCnJlcXVpcmUoZ3QpICMgd2VsbCBmb3JtYXR0ZWQgdGFibGVzDQojIGNsaWVudC1zaWRlIGludGVyYWN0aXZlIHB1Ymxpc2hhYmxlIGdyYXBoaWNzDQpyZXF1aXJlKHBsb3RseSkNCnJlcXVpcmUobGVhZmxldCkNCnJlcXVpcmUoY3Jvc3N0YWxrKQ0KcmVxdWlyZShodG1sd2lkZ2V0cykNCiMgc2VydmVyLXNpZGUgaW50ZXJhY3RpdmUgZ3JhcGhpY3MNCnJlcXVpcmUoc2hpbnkpDQpyZXF1aXJlKHNoaW55anMpDQojIENhbm5lZCBJbnRlcmFjdGl2ZSBFREEgDQpyZXF1aXJlKEV4UGFuRGFSKQ0KDQoNCmBgYA0KIyMgRXhwbG9yaW5nIEtVIEJvb2sgUHJvY2Vzc2luZyBDaGFyZ2VzDQpgYGB7ciAgfQ0KIyByZWFkIEtVIGRhdGEgZnJhbWUNCktVYnBjLmRmIDwtIHJlYWRfY3N2KCJQdWJsaWMgRGF0YS9vcGVuYXBjLWRlL2RhdGEvYnBjLmNzdiIpDQojIHJlYWQgRE9BQiBtZXRhZGF0YQ0KDQpzb3VyY2UoJ1B1YmxpYyBEYXRhL0RPQUIvZG9hYmluZ2VzdC5SJykNCkRPQUJtZXRhLmRmIDwtIGRvYWJGZXRjaCgpDQpgYGANCmBgYHtyICB9DQoNCg0KaGVhZChLVWJwYy5kZikNCmhlYWQoc3VtbWFyeShLVWJwYy5kZikpDQoNCmdncGxvdChkYXRhID0gS1VicGMuZGYsIGFlcyhLVWJwYy5kZiRpbnN0aXR1dGlvbikpICsgZ2VvbV9iYXIoKSANCg0KZ2dwbG90KGRhdGEgPSBLVWJwYy5kZiwgYWVzKEtVYnBjLmRmJGV1cm8pKSArIGdlb21faGlzdG9ncmFtKCkNCg0KYGBgDQojIyBHZW5lcmFsIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMNCmBgYHtyICB9DQoNCmdncGxvdChkYXRhID0gS1VicGMuZGYpICsgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh4ID0gS1VicGMuZGYkZG9hYikpDQoNCiMgRGF0ZSB0byBEb2FiDQpkYXRlX2RvYWIgPC0gS1VicGMuZGYgJT4lIGdncGxvdChkYXRhID0gS1VicGMuZGYsIG1hcHBpbmcgPSBhZXMoeCA9IEtVYnBjLmRmJHBlcmlvZCwgY29sb3VyID0gS1VicGMuZGYkZG9hYikpICsgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDAuMSkNCmdncGxvdGx5KGRhdGVfZG9hYikNCg0KIyBwdWJsaXNoZXJfZXVybyA8LSBLVWJwYy5kZiAlPiUgDQojIGdncGxvdChkYXRhID0gS1VicGMuZGYsIG1hcHBpbmcgPSBhZXMoeCA9IEtVYnBjLmRmJHB1Ymxpc2hlciwgY29sb3VyID0gS1VicGMuZGYkZXVybykpICsgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDAuMSkNCg0KIyBJbnN0aXR1dGlvbiB0byBFdXJvDQppbnN0aXR1dGlvbl9ldXJvIDwtIEtVYnBjLmRmICU+JSBnZ3Bsb3QoZGF0YSA9IEtVYnBjLmRmLCBtYXBwaW5nID0gYWVzKHggPSBLVWJwYy5kZiRldXJvKSkgKyBnZW9tX2ZyZXFwb2x5KG1hcHBpbmcgPSBhZXMoY29sb3VyID0gS1VicGMuZGYkaW5zdGl0dXRpb24pLCBiaW53aWR0aCA9IDUwMCkNCg0KZ2dwbG90bHkoaW5zdGl0dXRpb25fZXVybykNCg0KYGBgDQojIyBJZGVhOiBQdWJsaXNoZXJzIHZzLiBDaGFyZ2VzDQojIyBRdWVzdGlvbjogSG93IGRvIHRoZSB0b3AgMjUlIG9mIHB1Ymxpc2hlcnMgZGl2aWRlIHVwIGNoYXJnZXMgKGluIEV1cm8pPw0KIyMgT2JzZXJ2YXRpb246IENoYXJnZXMgYXJlIGdyb3VwZWQgYXJvdW5kIH4yMDAwIEV1cm9zIGFuZCB+ODAwMCBFdXJvcy4gDQpgYGB7ciAgfQ0KDQpwdWJsaXNoZXJfY291bnRzIDwtIEtVYnBjLmRmICU+JQ0KICAgIGdyb3VwX2J5KHB1Ymxpc2hlcikgJT4lDQogICAgdGFsbHkNCg0Kc29ydGVkX2NvdW50cyA9IGFycmFuZ2UocHVibGlzaGVyX2NvdW50cywgZGVzYyhuKSkNCg0KdG90YWxfbiA9IHN1bShzb3J0ZWRfY291bnRzJG4pDQpxdWFydGVyX24gPSAwLjI1ICogdG90YWxfbg0KbmV3X24gPSBzdW0oc29ydGVkX2NvdW50cyRuWzA6Nl0pDQoNCnNvcnRlZF9jb3VudHMgJT4lIGZpbHRlcihuID4gMjQpDQoNCiMgZmlsdGVyZWQgPC0gZmlsdGVyKEtVYnBjLmRmJHB1Ymxpc2hlciAlaW4lIHNvcnRlZF9jb3VudHMkcHVibGlzaGVyKQ0KDQpmaWx0ZXJlZCA8LSBmaWx0ZXIoS1VicGMuZGYsIEtVYnBjLmRmJHB1Ymxpc2hlciA9PSAndHJhbnNjcmlwdCBWZXJsYWcnIHwNCiAgICAgICAgICAgICAgICAgICAgIEtVYnBjLmRmJHB1Ymxpc2hlciA9PSAnRHVrZSBVbml2ZXJzaXR5IFByZXNzJyB8DQogICAgICAgICAgICAgICAgICAgICBLVWJwYy5kZiRwdWJsaXNoZXIgPT0gJ1VuaXZlcnNpdHkgb2YgTWljaGlnYW4gUHJlc3MnIHwNCiAgICAgICAgICAgICAgICAgICAgIEtVYnBjLmRmJHB1Ymxpc2hlciA9PSAnTWFuY2hlc3RlciBVbml2ZXJzaXR5IFByZXNzJyB8DQogICAgICAgICAgICAgICAgICAgICBLVWJwYy5kZiRwdWJsaXNoZXIgPT0gJ1BsdXRvIFByZXNzJyB8DQogICAgICAgICAgICAgICAgICAgICBLVWJwYy5kZiRwdWJsaXNoZXIgPT0gJ0xpdmVycG9vbCBVbml2ZXJzaXR5IFByZXNzJykNCg0KaGVhZChmaWx0ZXJlZCkNCg0KZXVyb19wdWJsaXNoZXIgPC0gZmlsdGVyZWQgJT4lIA0KICBnZ3Bsb3QoZGF0YSA9IGZpbHRlcmVkLCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRwdWJsaXNoZXIsIHkgPSBmaWx0ZXJlZCRldXJvKSwgDQogICAgICAgICBhZXMoeCA9IGZpbHRlcmVkJHB1Ymxpc2hlciwgeSA9IGZpbHRlcmVkJGV1cm8pKSArIA0KICAjIGdlb21fY291bnQoYWVzKGNvbG9yID0gLi5uLi4sIHNpemUgPSBhZnRlcl9zdGF0KHByb3ApLCBncm91cCA9IGV1cm8pKSArIA0KICBnZW9tX2NvdW50KGFlcyhjb2xvciA9IC4ubi4uLCBncm91cCA9IGV1cm8pKSArIA0KICBzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSAxMCkgKyANCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKSArDQogIGxhYnModGl0bGUgPSAiSG93IFB1Ymxpc2hlcnMgRGl2aWRlIENoYXJnZXMiLCB4ID0gIlRvcCAyNSUgb2YgUHVibGlzaGVycyIsIHkgPSAiUHJpY2UgKEV1cm8pIiwgY29sb3IgPSAnTnVtYmVyIG9mIENvcGllcycpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcChzdHJfcmVwbGFjZV9hbGwoeCwgImZvbyIsICIgIiksIHdpZHRoID0gMTcpKQ0KDQojIGdncGxvdDoNCmdncGxvdGx5KGV1cm9fcHVibGlzaGVyKQ0KDQojIGNyb3NzdGFsazoNCmZ0IDwtIGhpZ2hsaWdodF9rZXkoZmlsdGVyZWQpDQpnZ19mdCA8LSBnZ3Bsb3QoZGF0YSA9IGZ0LCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRwdWJsaXNoZXIsIHkgPSBmaWx0ZXJlZCRldXJvKSkgKyANCiAgZ2VvbV9jb3VudChhZXMoY29sb3IgPSAuLm4uLiwgc2l6ZSA9IGFmdGVyX3N0YXQocHJvcCksIGdyb3VwID0gZXVybykpICsgDQogIGxhYnModGl0bGUgPSAiSG93IFB1Ymxpc2hlcnMgRGl2aWRlIENoYXJnZXMiLCB4ID0gIlRvcCAyNSUgb2YgUHVibGlzaGVycyIsIHkgPSAiUHJpY2UgKEV1cm8pIiwgY29sb3IgPSAnTnVtYmVyIG9mIENvcGllcycpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcChzdHJfcmVwbGFjZV9hbGwoeCwgImZvbyIsICIgIiksIHdpZHRoID0gMTcpKQ0KY3Jvc3NfZnQgPC0gYnNjb2xzKA0KICBmaWx0ZXJfc2VsZWN0KCJwdWJsaXNoZXIiLCAiU2VsZWN0IGEgcHVibGlzaGVyIiwgZnQsIH5wdWJsaXNoZXIpLA0KICBnZ3Bsb3RseShnZ19mdCwgZHluYW1pY1RpY2tzID0gVFJVRSksDQogIHdpZHRocyA9IGMoMTIsIDEyKQ0KKQ0KDQpic2NvbHMoY3Jvc3NfZnQpDQoNCiMgc2hhcmVkX2V1cm9fcHVibGlzaGVyIDwtIFNoYXJlZERhdGEkbmV3KGZpbHRlcmVkKQ0KIyBsZWFmbGV0KHNoYXJlZF9ldXJvX3B1Ymxpc2hlcikgJT4lIGFkZE1hcmtlcnMoKQ0KIyBkYXRhLnRhYmxlOjpkYXRhLnRhYmxlKHNoYXJlZF9ldXJvX3B1Ymxpc2hlcikNCg0KDQpgYGANCiMjIElkZWE6IFB1Ymxpc2hlcnMnIENoYXJnZXMgdnMuIFllYXIvT0EgVHlwZQ0KIyMgU3ViLVF1ZXN0aW9uOiBXaGF0IGJlc3QgZXhwbGFpbnMgdGhlIHBhcnRpY3VsYXIgZGl2aXNpb24gb2YgY2hhcmdlcz8gKFllYXIsIE9BIFR5cGUpDQojIyBPYnNlcnZhdGlvbjogVGhlIGxvdyBhbmQgaGlnaCBjaGFyZ2UgZ3JvdXBzIHNlZW0gdG8gYmUgZGVmaW5lZCBieSB0aGUgdHlwZSBvZiBPQSBidXNpbmVzcyBtb2RlbCwgd2hlcmVhcyB0aGUgc2xpZ2h0IGRpZmZlcmVuY2VzIHdpdGhpbiBlYWNoIGdyb3VwIHNlZW0gdG8gYmUgZGVmaW5lZCBieSB0aGUgeWVhci4gDQpgYGB7ciAgfQ0KDQpoZWFkKGZpbHRlcmVkKQ0KDQojIERvZXMgVHlwZSBvZiBPQSBpbXBhY3QgdGhlIHBhcnRpY3VsYXIgZGl2aXNpb24gb2YgY2hhcmdlcz8NCg0KZXVyb19vYV9wdWJsaXNoZXIgPC0gZmlsdGVyZWQgJT4lIA0KICBnZ3Bsb3QoZGF0YSA9IGZpbHRlcmVkLCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSwgeSA9IGZpbHRlcmVkJGV1cm8pLCANCiAgICAgICAgIGFlcyh4ID0gZmlsdGVyZWQkYmFja2xpc3Rfb2EsIHkgPSBmaWx0ZXJlZCRldXJvKSkgKyANCiAgZ2VvbV9jb3VudChhZXMoY29sb3IgPSAuLm4uLiwgZ3JvdXAgPSBldXJvKSkgKyANCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gMTApICsgDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkgKw0KICBsYWJzKHRpdGxlID0gIkhvdyBPQSBJbXBhY3RzIFByaWNlIERpdmlzaW9uIG9mIENoYXJnZXMiLCB4ID0gIlR5cGUgb2YgT0EiLCB5ID0gIlByaWNlIChFdXJvKSIsIGNvbG9yID0gJ051bWJlciBvZiBDb3BpZXMnKQ0KDQojIGdncGxvdDoNCmdncGxvdGx5KGV1cm9fb2FfcHVibGlzaGVyKQ0KDQojIGNyb3NzdGFsazoNCmZ0IDwtIGhpZ2hsaWdodF9rZXkoZmlsdGVyZWQpDQpnZ19mdCA8LSBnZ3Bsb3QoZGF0YSA9IGZ0LCBtYXBwaW5nID0gYWVzKHggPSBmaWx0ZXJlZCRiYWNrbGlzdF9vYSwgeSA9IGZpbHRlcmVkJGV1cm8pKSArIA0KICBnZW9tX2NvdW50KGFlcyhjb2xvciA9IC4ubi4uLCBzaXplID0gYWZ0ZXJfc3RhdChwcm9wKSwgZ3JvdXAgPSBldXJvKSkgKyANCiAgbGFicyh0aXRsZSA9ICJIb3cgT0EgSW1wYWN0cyBEaXZpc2lvbiBvZiBDaGFyZ2VzIiwgeCA9ICJUeXBlIG9mIE9BIiwgeSA9ICJQcmljZSAoRXVybykiLCBjb2xvciA9ICdOdW1iZXIgb2YgQ29waWVzJykNCmNyb3NzX2Z0IDwtIGJzY29scygNCiAgZmlsdGVyX3NlbGVjdCgicHVibGlzaGVyIiwgIlNlbGVjdCBhIHB1Ymxpc2hlciIsIGZ0LCB+cHVibGlzaGVyKSwNCiAgZ2dwbG90bHkoZ2dfZnQsIGR5bmFtaWNUaWNrcyA9IFRSVUUpLA0KICB3aWR0aHMgPSBjKDEyLCAxMikNCikNCg0KYnNjb2xzKGNyb3NzX2Z0KQ0KDQoNCiMgRG9lcyBZZWFyIGltcGFjdCB0aGUgcGFydGljdWxhciBkaXZpc2lvbiBvZiBjaGFyZ2VzPw0KDQpldXJvX3llYXJfcHVibGlzaGVyIDwtIGZpbHRlcmVkICU+JSANCiAgZ2dwbG90KGRhdGEgPSBmaWx0ZXJlZCwgbWFwcGluZyA9IGFlcyh4ID0gZmlsdGVyZWQkcGVyaW9kLCB5ID0gZmlsdGVyZWQkZXVybyksIA0KICAgICAgICAgYWVzKHggPSBmaWx0ZXJlZCRwZXJpb2QsIHkgPSBmaWx0ZXJlZCRldXJvKSkgKyANCiAgZ2VvbV9jb3VudChhZXMoY29sb3IgPSAuLm4uLiwgZ3JvdXAgPSBldXJvKSkgKyANCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gMTApICsgDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkgKw0KICBsYWJzKHRpdGxlID0gIkhvdyBZZWFyIEltcGFjdHMgUHJpY2UgRGl2aXNpb24gb2YgQ2hhcmdlcyIsIHggPSAiWWVhciIsIHkgPSAiUHJpY2UgKEV1cm8pIiwgY29sb3IgPSAnTnVtYmVyIG9mIENvcGllcycpDQoNCiMgZ2dwbG90Og0KZ2dwbG90bHkoZXVyb195ZWFyX3B1Ymxpc2hlcikNCg0KIyBjcm9zc3RhbGs6DQpmdCA8LSBoaWdobGlnaHRfa2V5KGZpbHRlcmVkKQ0KZ2dfZnQgPC0gZ2dwbG90KGRhdGEgPSBmdCwgbWFwcGluZyA9IGFlcyh4ID0gZmlsdGVyZWQkcGVyaW9kLCB5ID0gZmlsdGVyZWQkZXVybykpICsgDQogIGdlb21fY291bnQoYWVzKGNvbG9yID0gLi5uLi4sIHNpemUgPSBhZnRlcl9zdGF0KHByb3ApLCBncm91cCA9IGV1cm8pKSArIA0KICBsYWJzKHRpdGxlID0gIkhvdyBZZWFyIEltcGFjdHMgRGl2aXNpb24gb2YgQ2hhcmdlcyIsIHggPSAiWWVhciIsIHkgPSAiUHJpY2UgKEV1cm8pIiwgY29sb3IgPSAnTnVtYmVyIG9mIENvcGllcycpDQpjcm9zc19mdCA8LSBic2NvbHMoDQogIGZpbHRlcl9zZWxlY3QoInB1Ymxpc2hlciIsICJTZWxlY3QgYSBwdWJsaXNoZXIiLCBmdCwgfnB1Ymxpc2hlciksDQogIGdncGxvdGx5KGdnX2Z0LCBkeW5hbWljVGlja3MgPSBUUlVFKSwNCiAgd2lkdGhzID0gYygxMiwgMTIpDQopDQoNCmJzY29scyhjcm9zc19mdCkNCg0KDQpgYGANCiMjIElkZWE6IFB1Ymxpc2hlcnMgdnMuIE9BDQojIyBRdWVzdGlvbjogV2hhdCB0eXBlIG9mIGJ1c2luZXNzIG1vZGVsIGRvIHRoZSB0b3AgMjUlIHB1Ymxpc2hlcnMgdXNlPw0KIyMgT2JzZXJ2YXRpb246IE1vc3QgaGF2ZSBhIGhpZ2hlciBwcm9wb3J0aW9uIG9mIFRydWUgKG1vdmVkIHRvIE9BIGZyb20gdHJhZGl0aW9uYWwgcHVibGlzaGluZykgdGhhbiBGYWxzZSAoYWxyZWFkeSBwdWJsaXNoZWQgT0EpLg0KYGBge3IgIH0NCg0Kb2FfdHlwZSA8LSBmaWx0ZXJlZCAlPiUgDQogIGdncGxvdChkYXRhID0gZmlsdGVyZWQsIG1hcHBpbmcgPSBhZXMoeCA9IGZpbHRlcmVkJHB1Ymxpc2hlciwgY29sb3VyID0gZmlsdGVyZWQkYmFja2xpc3Rfb2EpLCBmaWxsID0gZmlsdGVyZWQkYmFja2xpc3Rfb2EpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIsIHdpZHRoID0gMC43LCBmaWxsPSIjRUFFQUVBIikgKw0KICBsYWJzKHRpdGxlID0gIkJ1c2luZXNzIE1vZGVsIE9BIGZvciBQdWJsaXNoZXJzIiwgeCA9ICJUb3AgMjUlIG9mIFB1Ymxpc2hlcnMiLCB5ID0gIlByb3BvcnRpb24iLCBjb2xvciA9ICdUeXBlcyBvZiBPQScpICsNCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKSArDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoc3RyX3JlcGxhY2VfYWxsKHgsICJmb28iLCAiICIpLCB3aWR0aCA9IDE3KSkgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikNCg0KZ2dwbG90bHkob2FfdHlwZSkNCg0KIyBjcm9zc3RhbGs6DQpmdCA8LSBoaWdobGlnaHRfa2V5KGZpbHRlcmVkKQ0Kb2FfZnQgPC0gZ2dwbG90KGRhdGEgPSBmdCwgbWFwcGluZyA9IGFlcyh4ID0gZnQkcHVibGlzaGVyLCBjb2xvdXIgPSBmdCRiYWNrbGlzdF9vYSksIGZpbGwgPSBmdCRiYWNrbGlzdF9vYSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIiwgd2lkdGggPSAwLjcpICsNCiAgbGFicyh0aXRsZSA9ICJCdXNpbmVzcyBNb2RlbCBPQSBmb3IgUHVibGlzaGVycyIsIHggPSAiVG9wIDI1JSBvZiBQdWJsaXNoZXJzIiwgeSA9ICJQcm9wb3J0aW9uIG9mIEJhY2tsaXN0IE9BIiwgY29sb3IgPSAnVHlwZXMgb2YgT0EnKSArDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHN0cl9yZXBsYWNlX2FsbCh4LCAiZm9vIiwgIiAiKSwgd2lkdGggPSAxNykpDQojIGNyb3NzX29hX2Z0IDwtIGJzY29scygNCiMgICBmaWx0ZXJfc2VsZWN0KCJwdWJsaXNoZXIiLCAiU2VsZWN0IGEgcHVibGlzaGVyIiwgZnQsIH5wdWJsaXNoZXIpLA0KIyAgIGdncGxvdGx5KG9hX2Z0LCBkeW5hbWljVGlja3MgPSBUUlVFKSwNCiMgICAjIHdpZHRocyA9IGMoMTIsIDEyKQ0KIyApDQoNCiMgYnNjb2xzKGNyb3NzX29hX2Z0KQ0KDQoNCmBgYA0KIyMgSWRlYTogUHVibGlzaGVycycgT0EgdnMuIFllYXINCiMjIFF1ZXN0aW9uOiBEaWQgT0EgYnVzaW5lc3MgbW9kZWxzIG9mIHRoZSB0b3AgMjUlIHB1Ymxpc2hlcnMgY2hhbmdlIHBlciB5ZWFyPw0KIyMgT2JzZXJ2YXRpb246DQpgYGB7ciAgfQ0KDQpvYV90aW1lIDwtIGZ1bmN0aW9uKHB1Yl9uYW1lKSB7DQogIHB1Yl9mdCA8LSBmaWx0ZXIoZmlsdGVyZWQsIGZpbHRlcmVkJHB1Ymxpc2hlciA9PSBwdWJfbmFtZSkNCiAgDQogIHB1Yl9vYSA8LSBwdWJfZnQgJT4lIA0KICAgIGdncGxvdChkYXRhID0gcHViX2Z0LCBtYXBwaW5nID0gYWVzKHggPSBwdWJfZnQkcGVyaW9kLCBjb2xvdXIgPSBwdWJfZnQkYmFja2xpc3Rfb2EpLCBmaWxsID0gcHViX2Z0JGJhY2tsaXN0X29hKSArDQogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIsIHdpZHRoID0gMC43LCBmaWxsPSIjRUFFQUVBIikgKw0KICAgIGxhYnModGl0bGUgPSBwYXN0ZShwdWJfbmFtZSwgIidzIE9BIFRocm91Z2ggdGhlIFllYXJzIiwgc2VwID0gIiIpLCANCiAgICAgICAgIHggPSAiWWVhcnMiLCB5ID0gIlByb3BvcnRpb24gb2YgQmFja2xpc3QgT0EiLCBjb2xvciA9ICdUeXBlcyBvZiBPQScpICsNCiAgICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpICsNCiAgICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cz1jKDIwMTcsIDIwMTgsIDIwMTkpKSArDQogICAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpDQoNCiAgZ2dwbG90bHkocHViX29hKQ0KICANCn0NCg0KdG9wMjVfbGlzdCA9IGMoInRyYW5zY3JpcHQgVmVybGFnIiwgIkR1a2UgVW5pdmVyc2l0eSBQcmVzcyIsICJVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIFByZXNzIiwgIk1hbmNoZXN0ZXIgVW5pdmVyc2l0eSBQcmVzcyIsICJQbHV0byBQcmVzcyIsICJMaXZlcnBvb2wgVW5pdmVyc2l0eSBQcmVzcyIpDQoNCm9hX3RpbWUoInRyYW5zY3JpcHQgVmVybGFnIikNCg0Kb2FfdGltZSgiRHVrZSBVbml2ZXJzaXR5IFByZXNzIikNCg0Kb2FfdGltZSgiVW5pdmVyc2l0eSBvZiBNaWNoaWdhbiBQcmVzcyIpDQoNCm9hX3RpbWUoIk1hbmNoZXN0ZXIgVW5pdmVyc2l0eSBQcmVzcyIpDQoNCm9hX3RpbWUoIlBsdXRvIFByZXNzIikNCg0Kb2FfdGltZSgiTGl2ZXJwb29sIFVuaXZlcnNpdHkgUHJlc3MiKQ0KDQpgYGANCiMjIElkZWE6IFJldmVudWUgdnMuIE9BDQojIyBRdWVzdGlvbjogV2hhdCB0b3RhbCByZXZlbnVlIGFyZSBwdWJsaXNoZXJzIHJlY2VpdmluZyBlYWNoIHllYXI/DQojIyBPYnNlcnZhdGlvbjogDQpgYGB7ciAgfQ0KDQojIEZpbmRpbmcgdG90YWwgcmV2ZW51ZSBmb3IgZWFjaCBwdWJsaXNoZXINCg0KcmV2ZW51ZV9maW5kZXIgPC0gZnVuY3Rpb24ocHViX25hbWUpIHsNCiAgcHViX2ZpbHRlcmVkIDwtIGZpbHRlcihmaWx0ZXJlZCwgZmlsdGVyZWQkcHVibGlzaGVyID09IHB1Yl9uYW1lKQ0KICByZXYgPSBzdW0ocHViX2ZpbHRlcmVkJGV1cm8pDQp9DQoNCnJldmVudWVfZGYgPC0gZGF0YS5mcmFtZSgicHVibGlzaGVyIiA9IHRvcDI1X2xpc3QpDQpyZXZlbnVlX2xpc3QgPC0gYygpDQoNCmZvciAoaSBpbiB0b3AyNV9saXN0KSB7DQogIHJldmVudWVfbGlzdDwtYyhyZXZlbnVlX2xpc3QscmV2ZW51ZV9maW5kZXIoaSkpDQp9DQoNCnJldmVudWVfZGYkcmV2ZW51ZSA8LSBjKHJldmVudWVfbGlzdCkNCnByaW50KHJldmVudWVfZGYpDQoNCiMgZ2dwbG90Og0KcHVibGlzaGVyX3JldmVudWUgPC0gcmV2ZW51ZV9kZiAlPiUNCiAgZ2dwbG90KGRhdGEgPSByZXZlbnVlX2RmLCBtYXBwaW5nID0gYWVzKHggPSByZXZlbnVlX2RmJHB1Ymxpc2hlciwgeSA9IHJldmVudWVfZGYkcmV2ZW51ZSksIGZpbGwgPSByZXZlbnVlX2RmJHJldmVudWUpICsNCiAgZ2VvbV9jb2woKSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgUmV2ZW51ZSBmb3IgUHVibGlzaGVycyIsIHggPSAiVG9wIDI1JSBvZiBQdWJsaXNoZXJzIiwgeSA9ICJSZXZlbnVlIChFdXJvKSIsIGNvbG9yID0gJ1R5cGVzIG9mIE9BJykgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcChzdHJfcmVwbGFjZV9hbGwoeCwgImZvbyIsICIgIiksIHdpZHRoID0gMTcpKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpDQoNCmdncGxvdGx5KHB1Ymxpc2hlcl9yZXZlbnVlKQ0KDQoNCmBgYA0KIyMgSWRlYTogUmV2ZW51ZSB2cy4gT0ENCiMjIFF1ZXN0aW9uOiBXaGF0IHJldmVudWUgYXJlIHB1Ymxpc2hlcnMgcmVjZWl2aW5nIHBlciB5ZWFyPw0KIyMgT2JzZXJ2YXRpb246IA0KYGBge3IgIH0NCg0KIyBGaW5kaW5nIHRvdGFsIHJldmVudWUgZm9yIGVhY2ggcHVibGlzaGVyDQoNCnJldmxpc3RfMjAxNyA8LSBjKCkNCnJldmxpc3RfMjAxOCA8LSBjKCkNCnJldmxpc3RfMjAxOSA8LSBjKCkNCg0KcmV2bGlzdCA8LSBjKCkNCg0KZm9yIChuYW1lIGluIHRvcDI1X2xpc3QpIHsNCiAgcHViX25hbWUgPC0gZmlsdGVyKGZpbHRlcmVkLCBmaWx0ZXJlZCRwdWJsaXNoZXIgPT0gbmFtZSkNCiAgcmV2XzIwMTcgPSBzdW0ocHViX25hbWVbcHViX25hbWUkcGVyaW9kID09IDIwMTcsXSRldXJvKQ0KICByZXZsaXN0XzIwMTcgPC0gYyhyZXZsaXN0XzIwMTcsIHJldl8yMDE3KQ0KICByZXZfMjAxOCA9IHN1bShwdWJfbmFtZVtwdWJfbmFtZSRwZXJpb2QgPT0gMjAxOCxdJGV1cm8pDQogIHJldmxpc3RfMjAxOCA8LSBjKHJldmxpc3RfMjAxOCwgcmV2XzIwMTgpDQogIHJldl8yMDE5ID0gc3VtKHB1Yl9uYW1lW3B1Yl9uYW1lJHBlcmlvZCA9PSAyMDE5LF0kZXVybykNCiAgcmV2bGlzdF8yMDE5IDwtIGMocmV2bGlzdF8yMDE5LCByZXZfMjAxOSkNCn0NCg0KcmV2ZW51ZV9kZiA8LSBkYXRhLmZyYW1lKCJwdWJsaXNoZXIiID0gdG9wMjVfbGlzdCkNCnJldmVudWVfZGYkJzIwMTcnIDwtIGMocmV2bGlzdF8yMDE3KQ0KcmV2ZW51ZV9kZiQnMjAxOCcgPC0gYyhyZXZsaXN0XzIwMTgpDQpyZXZlbnVlX2RmJCcyMDE5JyA8LSBjKHJldmxpc3RfMjAxOSkNCg0KcHJpbnQocmV2ZW51ZV9kZikNCg0KcmV2ZW51ZV95ZWFyIDwtIGMocmV2ZW51ZV9kZiQnMjAxNycsIHJldmVudWVfZGYkJzIwMTgnLCByZXZlbnVlX2RmJCcyMDE5JykNCnllYXIgPC0gYygnMjAxNycsICcyMDE4JywgJzIwMTknKQ0KDQojIGdncGxvdDoNCnB1Yl95ZWFyX3JldmVudWUxIDwtIHJldmVudWVfZGYgJT4lDQogIA0KICBnZ3Bsb3QoZGF0YSA9IHJldmVudWVfZGYsIG1hcHBpbmcgPSBhZXMoeCA9ICcyMDE3JywgeSA9IHJldmVudWVfZGYkJzIwMTcnLCBmaWxsID0gcmV2ZW51ZV9kZiRwdWJsaXNoZXIpKSArDQogIGdlb21fYmFyKHBvc2l0aW9uPSJkb2RnZSIsIHN0YXQ9ImlkZW50aXR5IikgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIFJldmVudWUgZm9yIFB1Ymxpc2hlcnMiLCB4ID0gIlllYXIiLCB5ID0gIlJldmVudWUgKEV1cm8pIiwgY29sb3IgPSAnUHVibGlzaGVycycpICsNCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKQ0KDQpnZ3Bsb3RseShwdWJfeWVhcl9yZXZlbnVlMSkNCg0KcHViX3llYXJfcmV2ZW51ZTIgPC0gcmV2ZW51ZV9kZiAlPiUNCiAgDQogIGdncGxvdChkYXRhID0gcmV2ZW51ZV9kZiwgbWFwcGluZyA9IGFlcyh4ID0gJzIwMTgnLCB5ID0gcmV2ZW51ZV9kZiQnMjAxOCcsIGZpbGwgPSByZXZlbnVlX2RmJHB1Ymxpc2hlcikpICsNCiAgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgUmV2ZW51ZSBmb3IgUHVibGlzaGVycyIsIHggPSAiWWVhciIsIHkgPSAiUmV2ZW51ZSAoRXVybykiLCBjb2xvciA9ICdQdWJsaXNoZXJzJykgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpDQoNCmdncGxvdGx5KHB1Yl95ZWFyX3JldmVudWUyKQ0KDQpwdWJfeWVhcl9yZXZlbnVlMyA8LSByZXZlbnVlX2RmICU+JQ0KICANCiAgZ2dwbG90KGRhdGEgPSByZXZlbnVlX2RmLCBtYXBwaW5nID0gYWVzKHggPSAnMjAxOScsIHkgPSByZXZlbnVlX2RmJCcyMDE5JywgZmlsbCA9IHJldmVudWVfZGYkcHVibGlzaGVyKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpICsNCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBSZXZlbnVlIGZvciBQdWJsaXNoZXJzIiwgeCA9ICJZZWFyIiwgeSA9ICJSZXZlbnVlIChFdXJvKSIsIGNvbG9yID0gJ1B1Ymxpc2hlcnMnKSArDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuNzUpKSkNCg0KZ2dwbG90bHkocHViX3llYXJfcmV2ZW51ZTMpDQoNCnJldmVudWVfZGYgJT4lIGdhdGhlcignMjAxNycsJzIwMTgnLCcyMDE5Jywga2V5PSJ5ZWFyIiwgdmFsdWU9IkV1cm8iKSAlPiUgZ2dwbG90KGFlcyh4PXB1Ymxpc2hlcix5PUV1cm8pLGZpbGw9cHVibGlzaGVyKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKyBmYWNldF93cmFwKCJ5ZWFyIikNCg0KYGBgDQojIyMgQ29udGludWVkLCB0cmllZCBwdXR0aW5nIGl0IGludG8gb25lIGdyYXBoLiANCmBgYHtyfQ0KDQpyZXZsaXN0IDwtIGMoKQ0KcmV2bGlzdF8yMDE3IDwtIGMoKQ0KcmV2bGlzdF8yMDE4IDwtIGMoKQ0KcmV2bGlzdF8yMDE5IDwtIGMoKQ0KDQpmb3IgKG5hbWUgaW4gdG9wMjVfbGlzdCkgew0KICBwdWJfbmFtZSA8LSBmaWx0ZXIoZmlsdGVyZWQsIGZpbHRlcmVkJHB1Ymxpc2hlciA9PSBuYW1lKQ0KICByZXZfMjAxNyA9IHN1bShwdWJfbmFtZVtwdWJfbmFtZSRwZXJpb2QgPT0gMjAxNyxdJGV1cm8pDQogIHJldmxpc3RfMjAxNyA8LSBjKHJldmxpc3RfMjAxNywgcmV2XzIwMTcpDQogIHJldl8yMDE4ID0gc3VtKHB1Yl9uYW1lW3B1Yl9uYW1lJHBlcmlvZCA9PSAyMDE4LF0kZXVybykNCiAgcmV2bGlzdF8yMDE4IDwtIGMocmV2bGlzdF8yMDE4LCByZXZfMjAxOCkNCiAgcmV2XzIwMTkgPSBzdW0ocHViX25hbWVbcHViX25hbWUkcGVyaW9kID09IDIwMTksXSRldXJvKQ0KICByZXZsaXN0XzIwMTkgPC0gYyhyZXZsaXN0XzIwMTksIHJldl8yMDE5KQ0KfQ0KDQpyZXZsaXN0IDwtIGMocmV2bGlzdF8yMDE3LCByZXZsaXN0XzIwMTgsIHJldmxpc3RfMjAxOSkNCg0KcHJpbnQocmV2bGlzdCkNCg0KbnJldiA8LSBtYXRyaXgocmV2bGlzdCwgbmNvbD02LCBieXJvdz1UUlVFKQ0KY29sbmFtZXMobnJldikgPC0gdG9wMjVfbGlzdA0Kcm93bmFtZXMobnJldikgPC0gYygiMjAxNyIsICIyMDE4IiwgIjIwMTkiKQ0KbnJldiA8LSBhcy50YWJsZShucmV2KQ0KbnJldiA8LSBhcy5kYXRhLmZyYW1lLm1hdHJpeChucmV2KQ0KDQpwcmludChucmV2KQ0KDQojLCBucmV2JGBEdWtlIFVuaXZlcnNpdHkgUHJlc3NgLCBucmV2JGBVbml2ZXJzaXR5IG9mIE1pY2hpZ2FuIFByZXNzYCwgbnJldiRgUGx1dG8gUHJlc3NgLCBucmV2JGBNYW5jaGVzdGVyIFVuaXZlcnNpdHkgUHJlc3NgLCBucmV2JGBMaXZlcnBvb2wgVW5pdmVyc2l0eSBQcmVzc2ANCg0KcHViX3llYXJfcmV2IDwtIG5yZXYgJT4lDQogIA0KICBnZ3Bsb3QoZGF0YSA9IG5yZXYsIG1hcHBpbmcgPSBhZXMoeCA9IGMoIjIwMTciLCAiMjAxOCIsICIyMDE5IiksIHkgPSBjKG5yZXYkInRyYW5zY3JpcHQgVmVybGFnIiksIGZpbGwgPSBucmV2JHB1Ymxpc2hlcikpICsNCiAgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgUmV2ZW51ZSBmb3IgUHVibGlzaGVycyIsIHggPSAiWWVhciIsIHkgPSAiUmV2ZW51ZSAoRXVybykiLCBjb2xvciA9ICdQdWJsaXNoZXJzJykgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgwLjc1KSkpDQoNCmdncGxvdGx5KHB1Yl95ZWFyX3JldikNCg0KYGBgDQojIyBJZGVhOiBET0FCIGFuYWx5c2lzDQojIyBRdWVzdGlvbjogV2hhdCBpcyB0aGUgYXZlcmFnZSB0aW1lIGdhcCBiZXR3ZWVuIHllYXIgb2YgcHVibGljYXRpb24gYW5kIGFkZGVkIG9uIGRhdGU/IA0KIyMgT2JzZXJ2YXRpb246IA0KYGBge3J9DQoNCkRPQUJtZXRhLmRmIDwtIGZpbHRlcihET0FCbWV0YS5kZiwgaXMubmEoRE9BQm1ldGEuZGYkWWVhci5vZi5wdWJsaWNhdGlvbikpDQpwcmludChET0FCbWV0YS5kZiRZZWFyLm9mLnB1YmxpY2F0aW9uWzE6NF0pDQpnYXAgPSBtZWFuKERPQUJtZXRhLmRmJEFkZGVkLm9uLmRhdGUgLSBET0FCbWV0YS5kZiRZZWFyLm9mLnB1YmxpY2F0aW9uWzE6M10pDQpwcmludChnYXApDQoNCmBgYA0KIyMjIENvbXBhcmlzb24gb2YgY2hhcmdlcyBieSB5ZWFyIGFuZCBiYWNrbGlzdA0KYGBge3J9DQojIGNyZWF0ZSBmYWNldGVkIHBsb3Qgb2JqZWN0DQpjaGFyZ2VzLnBsb3QgPC0gS1VicGMuZGYgJT4lIGdncGxvdChhZXMoZXVybykpK2dlb21faGlzdG9ncmFtKGJpbnM9NikrZmFjZXRfZ3JpZChyb3dzPXZhcnMocGVyaW9kKSwgY29scyA9IHZhcnMoYmFja2xpc3Rfb2EpKQ0KDQoNCiMjIFByZXNlbnQgYXMgU3RhbmRhcmQgcGxvdA0KIHBsb3QoY2hhcmdlcy5wbG90KQ0KDQojIHRoaXMgcGxvdCB3aWxsIHJlbmRlciBwdWJsaWNseSBodHRwczovL2h0bWxwcmV2aWV3LmdpdGh1Yi5pby8/aHR0cHM6Ly9naXRodWIuY29tL01JVC1JbmZvcm1hdGljcy9tb25vZ3JhcGgvYmxvYi9tYXN0ZXIvMDAlMjBFREElMjBTdGFydC5uYi5odG1sDQoNCmBgYA0KIyMjIEludGVyYWN0aXZlIGNoYXJnZXMgZXhwbG9yYXRpb24NCmBgYHtyfQ0KIGdncGxvdGx5KGNoYXJnZXMucGxvdCkNCiMgaHR0cHM6Ly9taXQtaW5mb3JtYXRpY3MuZ2l0aHViLmlvL21vbm9ncmFwaC9kZW1vLmh0bWwNCg0KYGBgDQpgYGANCiMjIyBJbnRlcmFjdGl2ZSBEYXRhc2V0IEV4cGxvcmF0aW9uIA0KYGBgDQpgYGB7cn0NCktVYnBjLmRmICU+JSBFeFBhbkQoZGY9LiAgICAgICAsdGl0bGU9IktVIEJvb2sgUHJvY2Vzc2luZyBDaGFyZ2VzIixleHBvcnRfbmJfb3B0aW9uID0gVFJVRSkNCiMgRXhQYW5EIHVzZXMgc2hpbnkoKSB3aGljaCB3b3JrcyBydW5uaW5nIFIgbG9jYWxseSwgYnV0IGlzbid0IGdvaW5nIHRvIHdvcmsgdGhyb3VnaCBnaXRodWIuIENvdWxkIHB1Ymxpc2ggdGhyb3VnaCBzaGlueWFwcHMuaW8gKGxvdyB1c2FnZSBvbmx5KSwgb3IgZXhwb3J0ICBhIG5vbi1pbnRlcmFjdGl2ZSBub3RlYm9vayBpdA0KIyBzZWU6IGh0dHBzOi8vZHJtYWx0bWFuLnNoaW55YXBwcy5pby9kZW1vLw0KYGBgDQoNCg==